From eaadd141a9c07fb8604ad3e8d1461c217ce495d9 Mon Sep 17 00:00:00 2001 From: robertl Date: Mon, 5 Sep 2005 05:21:58 +0000 Subject: [PATCH] Ooops. Pick up lost 'add' from new msroute format. --- gpsbabel/msroute.c | 698 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 698 insertions(+) create mode 100644 gpsbabel/msroute.c diff --git a/gpsbabel/msroute.c b/gpsbabel/msroute.c new file mode 100644 index 000000000..d036348d1 --- /dev/null +++ b/gpsbabel/msroute.c @@ -0,0 +1,698 @@ +/* + + Support for Microsoft AutoRoute 2002 ".axe" files, + + Copyright (C) 2005 Olaf Klein, o.b.klein@t-online.de + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + +*/ + +#include "defs.h" +#include "jeeps/gpsmath.h" +#include + +#define MYNAME "msroute" + +#undef OLE_DEBUG + +FILE *fin; +char *fin_name; + +static arglist_t msroute_args[] = +{ + {0, 0, 0, 0, 0 } +}; + +/* MS-AutoRoute structures */ + +typedef struct msroute_head_s +{ + gbuint32 U1; /* 58/02/00/00 */ + char masm[4]; /* "MASM " */ + gbuint32 U2; + gbuint32 U3; + gbuint32 waypts; + gbuint32 U5; + gbuint32 U6; + gbuint32 U7; + gbuint32 U8; + gbuint32 U9; + gbuint32 U10; + gbuint32 U11; + gbuint32 U12; + gbuint32 U13; + gbuint32 U14; + gbuint32 U15; + gbuint32 U16; +// short U17; +// char U18; +} msroute_head_t; + +#define MSROUTE_OBJ_NAME "Journey" + +/* simple ole file reader */ + +#define OLE_MAX_NAME_LENGTH 32 +#define OLE_HEAD_FAT1_CT (512-0x4c)/4 + +#define BLOCKS(a, b) (((a) + (b) - 1) / (b)) + +static const char ole_magic[8] = +{ + 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 +}; + +/* + The ole implementation looks like a FAT filesystem. + Thatswhy i use in code fat1 as item for the "big blocks" or bbd + and fat2 for "small blocks" (sbd). + + Remarks: + + * in the moment ole_size1 and sector_size represents the same value + * in OLE_DEBUG mode: successfully tested with 64MB++ standard MS doc's (PowerPoint, Word) +*/ + +typedef struct ole_head_s +{ + char magic[8]; + char clsid[16]; + gbuint16 rev; /* offset 0x18 */ + gbuint16 ver; /* offset 0x1a */ + gbuint16 byte_order; /* offset 0x1c */ + gbuint16 fat1_size_shift; /* offset 0x1e */ + gbuint16 fat2_size_shift; /* offset 0x20 */ + gbuint16 U7; /* offset 0x22 */ + gbuint32 U8; /* offset 0x24 */ + gbuint32 U9; /* offset 0x28 */ + gbuint32 fat1_blocks; /* offset 0x2c */ + gbuint32 prop_start; /* offset 0x30 */ + gbuint32 U12; /* offset 0x34 */ + gbuint32 fat1_min_size; /* offset 0x38 */ + gbuint32 fat2_start; /* offset 0x3c */ + gbuint32 fat2_blocks; /* offset 0x40 */ + gbuint32 fat1_extra_start; /* offset 0x44 */ + gbuint32 fat1_extra_ct; /* offset 0x48 */ + gbuint32 fat1[OLE_HEAD_FAT1_CT]; /* offset 0x4c */ +} ole_head_t; + +typedef struct ole_prop_s +{ + gbuint16 name[32]; + gbuint16 name_size; /* offset 0x40 */ + char ole_typ; /* offset 0x42 */ + char U1; /* offset 0x43 */ + gbuint32 previous; /* offset 0x44 */ + gbuint32 next; /* offset 0x48 */ + gbuint32 dir; /* offset 0x4c */ + gbuint32 U5; /* offset 0x50 */ + gbuint32 U6; /* offset 0x54 */ + gbuint32 U7; /* offset 0x58 */ + gbuint32 U8; /* offset 0x5c */ + gbuint32 U9; /* offset 0x60 */ + gbuint32 U10; /* offset 0x64 */ + gbuint32 U11; /* offset 0x68 */ + gbuint32 U12; /* offset 0x6c */ + gbuint32 U13; /* offset 0x70 */ + gbuint32 first_sector; /* offset 0x74 */ + gbuint32 length; /* offset 0x78 */ + gbuint32 U16; /* offset 0x7c */ +} ole_prop_t; + +#define DIR_ITEM_SIZE sizeof(ole_prop_t) + +static int sector_size = 512; + +#define min(a,b) ((a) < (b)) ? (a) : (b) +#define max(a,b) ((a) > (b)) ? (a) : (b) + +static int *ole_fat1 = NULL; +static int *ole_fat2 = NULL; +static int ole_fat1_ct; +static int ole_fat2_ct; +static int ole_size1; +static int ole_size2; +static int ole_size1_min = 4096; +static ole_prop_t *ole_dir = NULL; +static int ole_dir_ct; +static ole_prop_t *ole_root = NULL; +static char **ole_root_sec = NULL; +static int ole_root_sec_ct; + +/* local helpers */ + +static void +is_fatal(const int condition, const char *fmt, ...) +{ + va_list args; + char buff[128]; + + if (condition == 0) return; + + va_start(args, fmt); + vsnprintf(buff, sizeof(buff), fmt, args); + va_end(args); + + fatal(MYNAME ": %s\n", buff); +} + +static void +print_buff(const char *buff, int count, const char *comment) +{ + int i; + printf(MYNAME ": dump of %s : ", comment); + for (i = 0; i < count; i++) + { + printf("%02x ", buff[i] & 0xFF); + } + printf("\n"); + fflush(stdout); +} + +static void +le_read32_buff(int *buff, const int count) +{ + int i; + for (i = 0; i < count; i++) + buff[i] = le_read32(&buff[i]); +} + +/* simple OLE file reader */ + +static void +ole_read_sector(const int sector, void *target) +{ + int res; + + res = fseek(fin, (sector + 1) * sector_size, SEEK_SET); + is_fatal((res != 0), "Could not seek file to sector %d!", sector + 1); + res = fread(target, 1, sector_size, fin); + is_fatal((res < sector_size), "Read error (%d, sector %d) on file \"%s\"!", res, sector, fin_name); +} + +static ole_prop_t * +ole_find_property(const char *property) +{ + int i; + + for (i = 0; i < ole_dir_ct; i++) + { + int j, len; + char buff[OLE_MAX_NAME_LENGTH + 1]; + ole_prop_t *item; + + item = &ole_dir[i]; + len = min(OLE_MAX_NAME_LENGTH, item->name_size / 2); + + for (j = 0; j < len; j++) + buff[j] = item->name[j]; + buff[j] = '\0'; + + if (case_ignore_strcmp(buff, property) == 0) + return item; + } + is_fatal((1), "\"%s\" not in property catalog!", property); +} + +static char * +ole_read_stream(const ole_prop_t *property) +{ + const char *action = "ole_read_stream"; + int len, sector, big, blocksize, offs, left; + int i; + int *fat; + char *buff; + + len = property->length; + + if (len >= ole_size1_min) + { + big = 1; + blocksize = ole_size1; + fat = ole_fat1; + } + else + { + big = 0; + blocksize = ole_size2; + fat = ole_fat2; + } + + offs = 0; + left = len; + sector = property->first_sector; + + i = ((len + blocksize - 1) / blocksize) * blocksize; + buff = xmalloc(i); /* blocksize aligned */ + + if (big != 0) + { + while (left > 0) + { + int bytes = (left <= blocksize) ? left : blocksize; + ole_read_sector(sector, buff + offs); + left -= bytes; + offs += bytes; + if (left > 0) + { + sector = fat[sector]; + is_fatal((sector < 0), "Broken stream (%s)!", action); + } + } + } + else + { + int chain = sector; + int blocks = (len + blocksize - 1) / blocksize; + int blocks_per_sector = sector_size / blocksize; + + offs = 0; + + while (blocks-- > 0) + { + char *temp; + int block_offs; + + is_fatal((chain < 0), "Broken stream (%s)!", action); + + sector = chain / blocks_per_sector; + is_fatal((sector >= ole_root_sec_ct), "Broken stream (%s)!", action); + + temp = ole_root_sec[sector]; + is_fatal((temp == NULL), "Broken stream (%s)!", action); + + block_offs = (chain % blocks_per_sector) * blocksize; + + memcpy(buff + offs, temp + block_offs, blocksize); + + offs += blocksize; + chain = fat[chain]; + } + } + return buff; +} + + +static char * +ole_read_property_stream(const char *property_name, int *length) +{ + ole_prop_t *property; + char *result; + + if ((property = ole_find_property(property_name)) == NULL) return NULL; + + result = ole_read_stream(property); + if ((result != NULL) && (length != NULL)) + *length = property->length; + + return result; +} + +#ifdef OLE_DEBUG + +static void +ole_test_properties() +{ + int i; + + for (i = 0; i < ole_dir_ct; i++) + { + char *temp; + char name[OLE_MAX_NAME_LENGTH + 1]; + ole_prop_t *p = &ole_dir[i]; + + if ((p->ole_typ != 1) && (p->ole_typ != 2) && (p->ole_typ != 5)) continue; + if ((p->length <= 0) || (p->name_size <= 0)) continue; + + temp = cet_str_uni_to_utf8(&p->name, min(p->name_size / 2, OLE_MAX_NAME_LENGTH)); + strncpy(name, temp, sizeof(name)); + xfree(temp); + + printf(MYNAME ": ole_test_properties for \"%s\" (%d bytes):", name, p->length); + + if ((case_ignore_strcmp(name, "Root Entry") == 0) || + (p->length < ole_size1_min)) + { + printf(" skipped...\n"); + continue; + } + else + { + int sector = p->first_sector; + int length = p->length; + int block_size = ole_size1; /* sector_size */ + + printf("\n"); + + while ((length > 0) && (sector >= 0)) + { + int bytes = (length > block_size) ? block_size : length; + int prev = sector; + + length -= bytes; + sector = ole_fat1[sector]; + if (sector == -3) + { + printf(MYNAME ": special block at %d\n", prev); + if ((prev + 2) < ole_fat1_ct) + sector = ole_fat1[prev + 1]; + printf(MYNAME "-new sector: %d\n", sector); + } + } + is_fatal((length != 0), "Error in fat1 chain, sector = %d, %d bytes (=%d blocks) left!", + sector, length, BLOCKS(length, block_size)); + } + } +} +#endif + +static void +ole_init(void) +{ + ole_head_t head; + int i, i_offs, sector, count, left; + int fat1_extra[128]; + + ole_fat1 = NULL; + ole_fat2 = NULL; + + sector_size = 512; /* fixed for the moment */ + + is_fatal((sizeof(head) != sector_size), "(!) internal error - invalid header size (%d)!", sizeof(head)); + + memset(&head, 0, sizeof(head)); + fread(&head, sizeof(head), 1, fin); + + is_fatal((strncmp(head.magic, ole_magic, sizeof(ole_magic)) != 0), "No MS document."); + + head.rev = le_read16(&head.rev); + head.ver = le_read16(&head.ver); + head.byte_order = le_read16(&head.byte_order); + head.fat1_size_shift = le_read16(&head.fat1_size_shift); + head.fat2_size_shift = le_read16(&head.fat2_size_shift); + head.fat1_blocks = le_read32(&head.fat1_blocks); + head.prop_start = le_read32(&head.prop_start); + head.fat1_min_size = le_read32(&head.fat1_min_size); + head.fat2_start = le_read32(&head.fat2_start); + head.fat2_blocks = le_read32(&head.fat2_blocks); + head.fat1_extra_start = le_read32(&head.fat1_extra_start); + head.fat1_extra_ct = le_read32(&head.fat1_extra_ct); + le_read32_buff(&head.fat1[0], OLE_HEAD_FAT1_CT); + + ole_size1 = (1 << head.fat1_size_shift); + ole_size2 = (1 << head.fat2_size_shift); + ole_size1_min = head.fat1_min_size; + +#ifdef OLE_DEBUG + printf(MYNAME "-head: (version.revision) = %d.%d\n", head.ver, head.rev); + printf(MYNAME "-head: byte-order = %d\n", head.byte_order); + printf(MYNAME "-head: big fat start sector = %d (0x%x)\n", head.fat1[0], (head.fat1[0] + 1) * 512); + printf(MYNAME "-head: big fat blocks = %d\n", head.fat1_blocks); + printf(MYNAME "-head: big fat block size = %d\n", (1 << head.fat1_size_shift)); + printf(MYNAME "-head: small fat start sector = %d\n", head.fat2_start); + printf(MYNAME "-head: small fat blocks = %d\n", head.fat2_blocks); + printf(MYNAME "-head: small fat block size = %d\n", (1 << head.fat2_size_shift)); + printf(MYNAME "-head: big fat minimum length = %d\n", head.fat1_min_size); + printf(MYNAME "-head: property catalog start sector = %d\n", head.prop_start); + printf(MYNAME "-head: additional big fat blocks = %d\n", head.fat1_extra_ct); + printf(MYNAME "-head: additional big fat start sector = %d (0x%x)\n", head.fat1_extra_start, (head.fat1_extra_start + 1) * 512); +#endif + + is_fatal((head.byte_order != -2), "Unsupported byte-order %d", head.byte_order); +#if 0 + sector_size = ole_size1; /* i'll implement this, if i get an MS-doc (ole) */ + /* with "sector_size" other than 512 */ +#else + is_fatal((ole_size1 != 512), "Unsupported sector size %d", ole_size1); +#endif + ole_fat1 = xmalloc(head.fat1_blocks * sector_size); + ole_fat1_ct = (head.fat1_blocks * sector_size) / sizeof(int); + +#ifdef OLE_DEBUG + printf(MYNAME "-big fat: %d maximum sectors, size in memory %d, max. datasize %d bytes\n", + ole_fat1_ct, head.fat1_blocks * sector_size, head.fat1_blocks * sector_size * sector_size / sizeof(int)); +#endif + + i_offs = 0; /* load "big fat" into memory */ + left = head.fat1_blocks; + count = (left > OLE_HEAD_FAT1_CT) ? OLE_HEAD_FAT1_CT : left; + + for (i = 0; i < count; i++) + { + sector = head.fat1[i]; + ole_read_sector(sector, &ole_fat1[i_offs]); + i_offs += ole_size1 / 4; + } + + left -= count; + + if (left > 0) + { + sector = head.fat1_extra_start; + + while ((left > 0) && (sector >= 0)) + { + ole_read_sector(sector, &fat1_extra); + le_read32_buff(&fat1_extra[0], 128); + + count = (left < 127) ? left : 127; + for (i = 0; i < count; i++) + { + ole_read_sector(fat1_extra[i], &ole_fat1[i_offs]); + i_offs += ole_size1 / 4; + } + left -= count; + if (left > 0) + sector = fat1_extra[127]; + } + is_fatal((left > 0), "Broken stream!"); + } + if (ole_fat1_ct > 0) + le_read32_buff(&ole_fat1[0], ole_fat1_ct); + + + /* load fat2 "small fat" into memory */ + + sector = head.fat2_start; + if (sector >= 0) + { + count = 0; + do + { + if (ole_fat2 == NULL) + ole_fat2 = (int *)xmalloc((count + 1) * sector_size); + else + ole_fat2 = (int *)xrealloc(ole_fat2, (count + 1) * sector_size); + + ole_read_sector(sector, (char *)ole_fat2 + (count * sector_size)); + sector = ole_fat1[sector]; + + count++; + } + while (sector >= 0); + + ole_fat2_ct = (count * sector_size) / sizeof(int); + if (ole_fat2_ct > 0) + le_read32_buff(&ole_fat2[0], ole_fat2_ct); + } + + /* load directory (property catalog) */ + + sector = head.prop_start; + is_fatal((sector < 0), "Invalid file (no property catalog)!"); + + count = 0; + while (sector >= 0) + { + if (ole_dir == NULL) + ole_dir = (void *)xmalloc((count + 1) * sector_size); + else + ole_dir = (void *)xrealloc(ole_dir, (count + 1) * sector_size); + + ole_read_sector(sector, (char *)ole_dir + (count * sector_size)); + sector = ole_fat1[sector]; + + count++; + } + ole_dir_ct = (count * sector_size) / sizeof(ole_prop_t); + + /* fix endianess of property catalog */ + + for (i = 0; i < ole_dir_ct; i++) + { + ole_prop_t *item = &ole_dir[i]; + + item->first_sector = le_read32(&item->first_sector); + item->length = le_read32(&item->length); + } + + ole_root = ole_find_property("Root Entry"); + + /* read fat2 data sectors given by "Root Entry" */ + + ole_root_sec_ct = (ole_root->length + (sector_size - 1)) / sector_size; + ole_root_sec = xcalloc(ole_root_sec_ct + 1, sizeof(char *)); + + i = 0; + sector = ole_root->first_sector; + while (sector >= 0) + { + char *temp; + + temp = ole_root_sec[i++] = xmalloc(sector_size); + + ole_read_sector(sector, temp); + sector = ole_fat1[sector]; + } +#ifdef OLE_DEBUG + ole_test_properties(); +#endif +} + +static void +ole_deinit(void) +{ + if (ole_root_sec != NULL) + { + int i; + for (i = 0; i < ole_root_sec_ct; i++) + { + char *c; + if ((c = ole_root_sec[i])) xfree(c); + } + xfree(ole_root_sec); + } + if (ole_fat1 != NULL) xfree(ole_fat1); + if (ole_fat2 != NULL) xfree(ole_fat2); + if (ole_dir != NULL) xfree(ole_dir); +} + +/* global MS AutoRoute functions */ + +static void +msroute_read_journey(void) +{ + int len; + char *buff; + + buff = ole_read_property_stream(MSROUTE_OBJ_NAME, &len); + + if ((buff != NULL) && (len > 0)) + { + msroute_head_t *head = (msroute_head_t *)buff; + char *cin; + int len; + char text[256]; + int count = 0; + route_head *route; + waypoint *wpt; + + is_fatal((strncmp(head->masm, "MASM", 4) != 0), "Invalid or unknown data!"); + + cin = buff + 71; // sizeof(msroute_head_t); + + route = route_head_alloc(); + route_add_head(route); + + head->waypts = le_read32(&head->waypts); + + while (count < head->waypts) + { + double lat, lon; + short test; + + /* tested, but undocumented */ + is_fatal(((*cin != 0x23) && (*cin != 0x24)), "Invalid or unknown data!"); + cin++; + + len = *cin++; + strncpy(text, cin, len); + text[len] = '\0'; + + cin += len + 1; + test = le_read16(cin); + is_fatal((test != -2), "Unsupported byte order within data (%d).", test); + + cin += 2; + + len = *cin; /* skip wide-string 'name' */ + cin += (len * 2) + 1; + + cin += (5 * sizeof(int)); /* five unknown DWORDs */ + + /* offs 12 !!!! Latitude int32 LE */ + /* offs 16 !!!! Longitude int32 LE */ + + lat = GPS_Math_Semi_To_Deg(le_read32(cin+12)); + lon = GPS_Math_Semi_To_Deg(le_read32(cin+16)); + + cin += (23 * sizeof(int)); + cin += 3; + + count++; + + wpt = waypt_new(); + + wpt->latitude = lat; + wpt->longitude = lon; + wpt->shortname = xstrdup(text); +#ifdef OLE_DEBUG + waypt_add(waypt_dupe(wpt)); /* put to wpt-list to see results if no output is specified */ +#endif + route_add_wpt(route, wpt); + } + } + + if (buff != NULL) + xfree(buff); +} + +/* registered callbacks */ + +static void msroute_rd_init(const char *fname) +{ + fin_name = xstrdup(fname); + fin = xfopen(fname, "rb", MYNAME); + + ole_init(); +} + +static void msroute_rd_deinit(void) +{ + ole_deinit(); + + xfree(fin_name); + fclose(fin); +} + +static void msroute_read(void) +{ + msroute_read_journey(); +} + +ff_vecs_t msroute_vecs = { + ff_type_file, + { ff_cap_none, ff_cap_none, ff_cap_read }, + msroute_rd_init, + NULL, + msroute_rd_deinit, + NULL, + msroute_read, + NULL, + NULL, + msroute_args, + CET_CHARSET_MS_ANSI, 1 /* CET-REVIEW */ +}; -- 2.30.2